package spark.network.protocol;

import spark.network.Util;

import java.net.InetSocketAddress;

public class PreLoginResponse extends Response<PreLogin> {
	private static final Class<PreLogin> REQUEST_TYPE_CLASS = PreLogin.class;

	public static final int MESSAGE_LENGTH_MAX = Byte.MAX_VALUE;

	public static final boolean RELIABLE = true;
	public static final short TYPE = 0x03;
	private static final byte[] TYPE_BYTES = Util.makeBytes(TYPE);

	/**
	 * Validation number the client provided earlier.
	 */
	private long validation;
	public static final int VALIDATION_OFFSET = Response.SIZE;
	public static final int VALIDATION_LENGTH = Long.SIZE / 8;

	/**
	 * 0    if we declined the prelogin for whatever reason
	 * !=0  if we accepted
	 */
	private byte accepted;
	public static final int ACCEPTED_OFFSET = VALIDATION_OFFSET + VALIDATION_LENGTH;
	public static final int ACCEPTED_LENGTH = Byte.SIZE / 8;

	/**
	 * Length of the message contained within this package
	 */
	private byte messageLength;
	public static final int MESSAGE_LENGTH_OFFSET = ACCEPTED_OFFSET + ACCEPTED_LENGTH;
	public static final int MESSAGE_LENGTH_LENGTH = Byte.SIZE / 8;

	/**
	 * Reason for rejection or something else we want to tell the client.
	 */
	private byte[] message;
	public static final int MESSAGE_OFFSET = MESSAGE_LENGTH_OFFSET + MESSAGE_LENGTH_LENGTH;

	private InetSocketAddress source;

	private static final int SIZE =
			Response.SIZE +
	        VALIDATION_LENGTH +
			ACCEPTED_LENGTH +
			MESSAGE_LENGTH_LENGTH;

	public PreLoginResponse() {
		super(RELIABLE, TYPE_BYTES, REQUEST_TYPE_CLASS);
	}

	public PreLoginResponse(PreLogin query) {
		super(RELIABLE, TYPE_BYTES, query, REQUEST_TYPE_CLASS);
		setValidation(query.getValidation());
	}

	public long getValidation() {
		return validation;
	}

	public void setValidation(long validation) {
		this.validation = validation;
	}

	public void setAccepted(boolean accepted) {
		this.accepted = (byte)(accepted ? 1 : 0);
	}

	public boolean getAccepted() {
		return accepted != 0;
	}

	public byte[] getMessage() {
		return message;
	}

	public InetSocketAddress getSource() {
		return source;
	}

	public void setSource(InetSocketAddress source) {
		this.source = source;
	}

	public void setMessage(String message) {
		byte[] messageBytes = message.getBytes();
		//set the length of the messageLength to the length of the provided string or to the maximal allowed length if the strings
		//length exceeds the maximum length
		messageLength = (byte)((messageBytes.length > MESSAGE_LENGTH_MAX) ? MESSAGE_LENGTH_MAX : messageBytes.length);

		//write nameLength characters into the name byte array
		this.message = new byte[messageLength];
		System.arraycopy(messageBytes, 0, this.message, 0, messageLength);
	}

	@Override
	public void getBytes(byte[] buffer) {
		super.getBytes(buffer);
		Util.arraycopy(Util.makeBytes(validation), 0, buffer, VALIDATION_OFFSET);
		buffer[ACCEPTED_OFFSET] = accepted;
		buffer[MESSAGE_LENGTH_OFFSET] = messageLength;
		if(message != null) {
			Util.arraycopy(message, 0, buffer, MESSAGE_OFFSET, messageLength);
		}
	}

	@Override
	public void fill(byte[] buffer) {
		super.fill(buffer);
		validation = Util.makeLong(buffer, VALIDATION_OFFSET);
		accepted = buffer[ACCEPTED_OFFSET];
		messageLength = buffer[MESSAGE_LENGTH_OFFSET];
		message = new byte[messageLength];
		Util.arraycopy(buffer, MESSAGE_OFFSET, message, 0, messageLength);
	}

	@Override
	public int getSize() {
		return SIZE + messageLength;
	}

	public static PreLoginResponse create(byte[] buffer, long receiveTime) {
		PreLoginResponse packet = new PreLoginResponse();
		packet.fill(buffer);
		packet.setTimeStampReceive(receiveTime);
		return packet;
	}
}